#include <string.h>
#include <stdio.h>
#include <stdlib.h>

char *trim(char *str)
{
  int i,j,len = strlen(str);

  for(i=0; i<len && isspace(str[i]); i++);
  for(j=len-1;j>i && isspace(str[j]);j--) str[j]=0;

  return &str[i];
}

char *readNonEmptyLine(FILE *f, char *buf, int bufLen)
{
  while(!feof(f)) {
    fgets(buf,bufLen,f);
    char *ptr = trim(buf);
    if(*ptr) return ptr;
  }

  return 0;
}

FILE *myfopen(char *name, char *extension, char *mode)
{
  int i;
  char buf[1024];

  sprintf(buf,"%s%s",name,extension);

  int len = strlen(buf);
  for(i=0;i<len;i++) buf[i] = tolower(buf[i]);

  FILE *f = fopen(buf,mode);

  return f;
}

void printEntityHeaderBegin(char *name, FILE *f)
{
  char headerBegin[] = 
"#ifndef %s_H\n" \
"#define %s_H\n" \
"\n" \
"#include \"entity.h\"\n" \
"#include <QObject>\n" \
"\n" \
"class %s : public Entity\n" \
"{\n" \
"    Q_OBJECT\n";

  fprintf(f,headerBegin,name,name,name);
}

void printEntityImplBegin(char *name, FILE *f)
{
  int i;

  char *lowerName = strdup(name);
  for(i=0; i<strlen(name); i++) lowerName[i] = tolower(lowerName[i]);

  fprintf(f,"#include \"%s.h\"\n\n",lowerName);

  free(lowerName);

  char constructor[] = 
"%s::%s(QObject *parent) :\n" \
"    Entity(parent)\n" \
"{\n}\n\n";

  fprintf(f,constructor,name,name);
}

void printQProperty(char *type, char *name, FILE *f)
{
  char *nameFirstUpper = strdup(name);
  nameFirstUpper[0] = toupper(nameFirstUpper[0]);

  fprintf(f,"    Q_PROPERTY(%s %s READ %s WRITE set%s NOTIFY %sChanged)\n",type,name,name,nameFirstUpper,name);

  free(nameFirstUpper);
}

void printGetSet(char *entityName, char *type, char *name, FILE *f)
{
  char get[] = 
"%s %s::%s() {\n" \
"    return this->m_%s;\n" \
"}\n\n";

  char set[] = 
"void %s::set%s(%s %s) {\n" \
"    if(this->m_%s != %s) {\n" \
"        this->m_%s = %s;\n" \
"        emit %sChanged();\n" \
"    }\n" \
"}\n\n";

  fprintf(f,get,type,entityName,name,name);

  char *nameFirstUpper = strdup(name);
  nameFirstUpper[0] = toupper(nameFirstUpper[0]);

  fprintf(f,set,entityName,nameFirstUpper,type,name,name,name,name,name,name);

  free(nameFirstUpper);
}

void printConstructorDecl(char *entityName, FILE *f)
{
  char constructor[] = "    %s(QObject *parent = 0);\n";


  fprintf(f,constructor,entityName);
}

void printGetSetDecl(char *type, char *name, FILE *f)
{
  char get[] = "    %s %s();\n";
  char set[] = "    void set%s(%s %s);\n\n";

  fprintf(f,get,type,name);

  char *nameFirstUpper = strdup(name);
  nameFirstUpper[0] = toupper(nameFirstUpper[0]);

  fprintf(f,set,nameFirstUpper,type,name);

  free(nameFirstUpper);
}

void printSignal(char *name, FILE *f)
{
  fprintf(f,"    void %sChanged();\n",name);
}

void printAttribute(char *type, char *name, FILE *f)
{
  fprintf(f, "    %s m_%s;\n",type,name);
}

void printDAO(char *entityName, FILE *f)
{
  char DAO[] = 
"#ifndef %sDAO_H\n" \
"#define %sDAO_H\n\n" \
"#include \"dao.h\"\n\n" \
"#include \"../entity/%s.h\"\n\n" \
"CUSTOM_DAO(%s, \"%s\", DEFAULT_PAGE_SIZE, DEFAULT_ID_COL_NAME, DEFAULT_FIND_QUERY, DEFAULT_COUNT_QUERY)\n\n" \
"#endif\n";

  int i;
  
  char *lowerEntityName = strdup(entityName);
  for(i=0; i<strlen(entityName); i++) lowerEntityName[i] = tolower(lowerEntityName[i]);

  fprintf(f,DAO,entityName,entityName,lowerEntityName,entityName,entityName);

  free(lowerEntityName);
}

void buildEntity(FILE *f)
{
  char nameBuf[1024],*name;
  char paramBuf[1024],*param;
  char typeBuf[1024],*type;

  name = readNonEmptyLine(f, nameBuf, 1024);
  if(!name) return;

  FILE *fHeader = myfopen(name,".h","w");
  FILE *fImpl = myfopen(name,".cpp","w");

  printEntityHeaderBegin(name,fHeader);
  printEntityImplBegin(name,fImpl);

  while(!feof(f))
  {
    type = readNonEmptyLine(f, typeBuf, 1024);
    if(!type) break;

    param = readNonEmptyLine(f, paramBuf, 1024);
    if(!param) break;

    printQProperty(type,param,fHeader);
    printGetSet(name,type,param,fImpl);
  }

  fprintf(fHeader,"\npublic:\n");
  printConstructorDecl(name,fHeader);

  fclose(fImpl);

  rewind(f);
  readNonEmptyLine(f, nameBuf, 1024);

  while(!feof(f))
  {
    type = readNonEmptyLine(f, typeBuf, 1024);
    if(!type) break;

    param = readNonEmptyLine(f, paramBuf, 1024);
    if(!param) break;

    printGetSetDecl(type,param,fHeader);
  }

  rewind(f);
  readNonEmptyLine(f, nameBuf, 1024);

  fprintf(fHeader,"signals:\n");

  while(!feof(f))
  {
    type = readNonEmptyLine(f, typeBuf, 1024);
    if(!type) break;

    param = readNonEmptyLine(f, paramBuf, 1024);
    if(!param) break;

    printSignal(param,fHeader);
  }

  rewind(f);
  readNonEmptyLine(f, nameBuf, 1024);

  fprintf(fHeader,"\npublic slots:\n\nprivate:\n");

  while(!feof(f))
  {
    type = readNonEmptyLine(f, typeBuf, 1024);
    if(!type) break;

    param = readNonEmptyLine(f, paramBuf, 1024);
    if(!param) break;

    printAttribute(type,param,fHeader);
  }

  fprintf(fHeader,"};\n\n#endif\n");

  fclose(fHeader);

  FILE *fDAO = myfopen(name,"dao.h","w");
  printDAO(name,fDAO);
  fclose(fDAO);

}

void main(int argc, char *argv[])
{
  int i;

  for(i=1;i<argc;i++) {
    FILE *f = fopen(argv[i],"r");

    if(f) {
      buildEntity(f);
      fclose(f);
    }
  }

}
